﻿
namespace Behemoth.SharePoint.Caml {

    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Text;

    using Microsoft.SharePoint;

    using CamlSchema = schemas.microsoft.com.sharepoint;
    using schemas.microsoft.com.sharepoint;

    /// <summary>
    /// To be implemented. The commented code in this class is for illustrative purposes only.
    /// </summary>
    public class CamlQueryBuilder {

        public static CamlQueryRoot Example_GetLinqXsdQuery(string fieldName, string compareValue) {
            LogicalTestDefinition test1 = new LogicalTestDefinition();
            test1.FieldRef.Add(new FieldRefDefinition(fieldName));
            test1.Value.Add(new ValueDefinition("Text", compareValue));

            CamlQueryRoot root = new CamlSchema.CamlQueryRoot();
            root.Where.Eq.Add(test1);
            return root;
        }

        /// <summary>
        /// Returns a basic CAML query strcture that can be used in list queries.
        /// You must provide the inner XML for the where clause.
        /// </summary>
        /// <param name="rowLimit">0 or the max number of rows to return</param>
        /// <param name="orderBy">Optional, internal name of fieldName to order results by</param>
        /// <param name="ascending">Ordering is ascending (true) or descending (false).</param>
        /// <param name="whereClause">The inner XML for the Where element (e.g. Eq Or And). Do not include the Where element itself.</param>
        /// <returns></returns>
        public static SPQuery BuildBaseQuery(int rowLimit, string orderBy, bool ascending, string whereClause) {
            return BuildBaseQuery(rowLimit, orderBy, ascending, whereClause, true);
        }
        public static SPQuery BuildBaseQuery(int rowLimit, string orderBy, bool ascending, string whereClause, bool doByStringConcatMethod)
        {
            SPQuery indexItemsQuery = new SPQuery();
            if (rowLimit > 0)
                indexItemsQuery.RowLimit = (uint)rowLimit;

            StringBuilder itemsQueryBuilder = new StringBuilder();
            if (doByStringConcatMethod) {
                if (!string.IsNullOrEmpty(orderBy))
                    itemsQueryBuilder.AppendFormat("<OrderBy><FieldRef Name=\"{0}\" Ascending=\"{1}\" /></OrderBy>", orderBy, ascending.ToString().ToUpper());
                if (!string.IsNullOrEmpty(whereClause)) {
                    itemsQueryBuilder.Append("<Where>");
                    itemsQueryBuilder.Append(whereClause);
                    itemsQueryBuilder.Append("</Where>");
                }
            } else {
                CamlQueryRoot qRoot = new CamlQueryRoot(orderBy, ascending);
                //qRoot.Where.Eq.Add();
                // TODO add some additional criteria to the query here
                if (!string.IsNullOrEmpty(orderBy))
                    itemsQueryBuilder.Append(qRoot.OrderBy.ToString());
                itemsQueryBuilder.Append(qRoot.Where.ToString());
            }
            indexItemsQuery.Query = itemsQueryBuilder.ToString();
            return indexItemsQuery;
        }

        /// <summary>
        /// Example CAML query clause compares a url fieldName to a web's URL.
        /// </summary>
        /// <param name="internalFieldName"></param>
        /// <param name="web"></param>
        /// <returns></returns>
        public static string GetWebUrlMatchClause(string internalFieldName, SPWeb web) {
            return string.Format("<Eq><FieldRef Name=\"{0}\"/><Value Type=\"URL\">{1}</Value></Eq>", internalFieldName, web.ServerRelativeUrl);
        }

        // TODO date compare

        /*
        public static SPQuery GetAllServiceRelatedCategories()
        {
            return GetAllServiceRelatedCategories(0, "Title", true);
        }
        public static SPQuery GetAllServiceRelatedCategories(int rowLimit)
        {
            return GetAllServiceRelatedCategories(rowLimit, "Title", true);
        }
        public static SPQuery GetAllServiceRelatedCategories(string orderBy, bool ascending)
        {
            return GetAllServiceRelatedCategories(0, orderBy, ascending);
        }
        public static SPQuery GetAllServiceRelatedCategories(int rowLimit, string orderBy, bool ascending)
        {
            SPQuery indexItemsQuery = new SPQuery();
            if (rowLimit > 0)
                indexItemsQuery.RowLimit = (uint)rowLimit;

            StringBuilder indexItemsQueryBuilder = new StringBuilder();
            indexItemsQueryBuilder.AppendFormat("<OrderBy><FieldRef Name=\"{0}\" Ascending=\"{1}\" /></OrderBy>", orderBy, ascending.ToString().ToUpper());
            indexItemsQueryBuilder.Append("<Where>");
            AppendStandardInternalNameWhereClause(indexItemsQueryBuilder);
            indexItemsQueryBuilder.Append("</Where>");

            indexItemsQuery.Query = indexItemsQueryBuilder.ToString();
            return indexItemsQuery;
        }

        public static SPQuery RecentlyModified(int rowLimit)
        {
            return GetAllServiceRelatedCategories(rowLimit, "Modified", false);
        }

        public static SPQuery MostPopular(int rowLimit)
        {
            return GetAllServiceRelatedCategories(rowLimit, ServicesPortalConstants.UniqueUsersLastMonthFieldInternalName, false);
        }

        public static SPQuery HasParent(SPListItem parentCategory, int rowLimit)
        {
            return HasParent(parentCategory, rowLimit, "Modified");
        }
        public static SPQuery HasParent(SPListItem parentCategory, int rowLimit, string orderBy)
        {
            if (parentCategory == null)
                throw new ArgumentNullException("parentCategory", "You must supply a list item for parentCategory.");
            SPQuery childrenQuery = new SPQuery();
            if (rowLimit > 0)
                childrenQuery.RowLimit = (uint)rowLimit;

            StringBuilder sb = new StringBuilder();
            sb.AppendFormat("<OrderBy><FieldRef Name=\"{0}\" Ascending=\"{1}\" /></OrderBy>", orderBy, false.ToString().ToUpper());
            sb.Append("<Where>");
            sb.Append("<Eq>");
            sb.Append("<FieldRef Name=\"Parent\" LookupId='TRUE' />");
            sb.AppendFormat("<Value Type=\"Lookup\">{0}</Value>", parentCategory.ID.ToString());
            sb.Append("</Eq>");
            sb.Append("</Where>");

            childrenQuery.Query = sb.ToString();
            return childrenQuery;
        }

        /// <summary>
        /// Appends the basic CAML Or clauses for InternalName is Service, SubService,
        /// or ServiceRedirect.
        /// </summary>
        /// <param name="sb"></param>
        public static void AppendStandardInternalNameWhereClause(StringBuilder sb)
        {
            sb.Append("<Or>");
            sb.Append("<Or>");
            sb.Append("<Eq>");
            sb.AppendFormat("<FieldRef Name=\"{0}\"/>", ServicesPortalConstants.CategoryTypeFieldInternalName);
            sb.AppendFormat("<Value Type=\"Text\">{0}</Value>", ServicesPortalConstants.CategoryTypeService);
            sb.Append("</Eq>");
            sb.Append("<Eq>");
            sb.AppendFormat("<FieldRef Name=\"{0}\"/>", ServicesPortalConstants.CategoryTypeFieldInternalName);
            sb.AppendFormat("<Value Type=\"Text\">{0}</Value>", ServicesPortalConstants.CategoryTypeSubService);
            sb.Append("</Eq>");
            sb.Append("</Or>");
            sb.Append("<Eq>");
            sb.AppendFormat("<FieldRef Name=\"{0}\"/>", ServicesPortalConstants.CategoryTypeFieldInternalName);
            sb.AppendFormat("<Value Type=\"Text\">{0}</Value>", ServicesPortalConstants.CategoryTypeServiceRedirect);
            sb.Append("</Eq>");
            sb.Append("</Or>");
        }

        public static SPQuery GetCurrent(SPWeb web)
        {
            if (web == null)
                throw new ArgumentNullException("web", "You must provide a valid web for the current Category.");
            SPQuery currentCategoryQuery = new SPQuery();
            currentCategoryQuery.RowLimit = 1;
            currentCategoryQuery.Query = string.Format("<Where><Eq><FieldRef Name=\"SiteURL\"/><Value Type=\"URL\">{0}</Value></Eq></Where>", web.ServerRelativeUrl);
            return currentCategoryQuery;
        }

        public static SPListItem GetChildCategory(SPList list, string parentCategoryId)
        {
            if (string.IsNullOrEmpty(parentCategoryId))
                return null;
            int parentId = 0;
            if (!int.TryParse(parentCategoryId, out parentId))
                return null;
            SPListItem parentCategory = null;
            try
            {
                parentCategory = list.GetItemById(parentId);
            }
            catch (Exception ex)
            {
                throw new IndexOutOfRangeException(string.Format("Category with ID '{0}' was not found. Exception was: {1}", parentId, ex.Message));
            }
            return parentCategory;
        }
        */

        /// <summary>
        /// Builds a query that will return all documents within a folder in a
        /// flattened view, and will optionally exclude a particular item by its ID.
        /// </summary>
        /// <param name="sourceFolder"></param>
        /// <param name="excludedItem"></param>
        /// <returns></returns>
        public static SPQuery BuildAllItemsInFolderExcept(SPFolder sourceFolder, SPListItem excludedItem) {
            if (sourceFolder.Item == null)
                throw new ArgumentException("sourceFolder does not have an Item property, therefore it is not part of a document library, which is not allowed for this method.", "sourceFolder");
            SPListItem sourceItem = sourceFolder.Item;
            if (excludedItem != null && excludedItem.ParentList.ID != sourceItem.ParentList.ID)
                throw new ArgumentException("excludedItem and sourceFolder come from different lists, which is not allowed for this method.", "excludedItem");
            // this should have the effect of returning all items if excludedItem is null
            StringBuilder sb = new StringBuilder();
            if (excludedItem != null) {
                sb.AppendFormat("<Neq>");
                sb.AppendFormat("  <FieldRef Name=\"{0}\"/>", "ID");
                sb.AppendFormat("  <Value Type=\"Counter\">{0}</Value>", excludedItem.ID);
                sb.AppendFormat("</Neq>");
            }
            SPQuery query = CamlQueryBuilder.BuildBaseQuery(0, string.Empty, true, sb.ToString());
            if (sourceFolder != null)
                query.Folder = sourceFolder;
            query.ViewAttributes += " Scope=\"Recursive\"";
            return query;
        }

    } // class

}
